package org.example.pglog.util;



import org.example.pglog.aop.FieleName;
import org.example.pglog.pojo.BeanDiff;
import org.example.pglog.pojo.FieldDiff;
import org.example.pglog.pojo.User;

import java.lang.reflect.Field;
import java.util.*;

/**
 * @author: 船长
 * @create: 2024-09-01 15:16
 **/

public class BeanCompareUtils {
    private static final String INCLUDE = "INCLUDE";
    private static final String EXCLUDE = "EXCLUDE";
    private static final String FILTER_TYPE = "FILTER_TYPE";
    private static final String FILTER_ARRAY = "FILTER_ARRAY";

    // 存放过滤类型及过滤字段数组
    private static ThreadLocal<Map<String, Object>> threadLocal = new ThreadLocal<Map<String, Object>>();

    public static void main(String[] args) {

        User oldUser = new User("1", "张三", 20);
        User newUser = new User("2", "李四", 21);
        System.out.println(oldUser.toString() + newUser);
//        oldUser.setAge();
//        newUser.setAge(15);

        String[] fieldArray = new String[]{"age","name","hobby"};

        BeanDiff beanDiff = compareInclude(oldUser, newUser,fieldArray);

        List<FieldDiff> list = beanDiff.getFieldDiffList();
        if (list != null && list.size() > 0) {
            for (int i = 0; i< list.size(); i ++) {
                FieldDiff fieldDiff = list.get(i);
                System.out.println(fieldDiff.toString());
            }
        }
    }

    /**
     * bean比较
     * @param oldBean
     * @param newBean
     * @return
     */
    public static BeanDiff compare(Object oldBean, Object newBean) {
        BeanDiff beanDiff = new BeanDiff();

        Class oldClass = oldBean.getClass();
        Class newClass = newBean.getClass();

        if (oldClass.equals(newClass)) {
            List<Field> fieldList = new ArrayList<Field>();
            fieldList = getCompareFieldList(fieldList, newClass);

            Map<String, Object> map = threadLocal.get();

            boolean needInclude = false;
            boolean needExclude = false;
            boolean hasArray = false;
            String[] fieldArray = null;

            if(map != null) {
                fieldArray = (String[])map.get(FILTER_ARRAY);
                String type = (String)map.get(FILTER_TYPE);

                if (fieldArray != null && fieldArray.length > 0) {
                    // 数组排序
                    Arrays.sort(fieldArray);
                    hasArray = true;

                    if (INCLUDE.equals(type)) {
                        needInclude = true;
                    } else if (EXCLUDE.equals(type)) {
                        needExclude = true;
                    }
                }
            }

            for (int i = 0; i < fieldList.size(); i ++) {
                Field field = fieldList.get(i);
                field.setAccessible(true);
                FieleName alias = field.getAnnotation(FieleName.class);

                try {
                    Object oldValue = field.get(oldBean);
                    Object newValue = field.get(newBean);

                    if (hasArray) {
                        // 二分法查找该字段是否被排除或包含
                        int idx = Arrays.binarySearch(fieldArray, field.getName());

                        // 该字段被指定排除或没有指定包含
                        if ((needExclude && idx > -1) || (needInclude && idx < 0)) {
                            continue;
                        }
                    }

                    if (nullableNotEquals(oldValue, newValue)) {
                        FieldDiff fieldDiff = new FieldDiff(field.getName(), alias.value(), oldValue, newValue);

                        // 打印
//                        System.out.println(fieldDiff.toString());

                        beanDiff.addFieldDiff(fieldDiff);
                    }

                } catch (IllegalArgumentException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }

        return beanDiff;
    }

    /**
     * bean比较
     * @param oldBean
     * @param newBean
     * @param includeFieldArray 需要包含的字段
     * @return
     */
    public static BeanDiff compareInclude(Object oldBean, Object newBean, String[] includeFieldArray) {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put(FILTER_TYPE, INCLUDE);
        map.put(FILTER_ARRAY, includeFieldArray);
        threadLocal.set(map);

        return compare(oldBean, newBean);
    }

    /**
     * bean比较
     * @param oldBean
     * @param newBean
     * @param excludeFieldArray 需要排除的字段
     * @return
     */
    public static BeanDiff compareExclude(Object oldBean, Object newBean, String[] excludeFieldArray) {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put(FILTER_TYPE, EXCLUDE);
        map.put(FILTER_ARRAY, excludeFieldArray);
        threadLocal.set(map);

        return compare(oldBean, newBean);
    }


    /**
     * 获取需要比较的字段list
     * @param fieldList
     * @param clazz
     * @return
     */
    private static List<Field> getCompareFieldList(List<Field> fieldList, Class clazz) {
        Field[] fieldArray = clazz.getDeclaredFields();

        List<Field> list = Arrays.asList(fieldArray);

        for (int i = 0; i < list.size(); i ++) {
            Field field = list.get(i);
            FieleName alias = field.getAnnotation(FieleName.class);
            if (alias != null) {
                fieldList.add(field);
            }
        }

        Class superClass = clazz.getSuperclass();
        if (superClass != null) {
            getCompareFieldList(fieldList, superClass);
        }
        return fieldList;
    }


    /**
     * 比较值是否不相等
     * @param oldValue
     * @param newValue
     * @return
     */
    private static boolean nullableNotEquals(Object oldValue, Object newValue) {

        if (oldValue == null && newValue == null) {
            return false;
        }

        if (oldValue != null && oldValue.equals(newValue)) {
            return false;
        }

        if (("".equals(oldValue) && newValue == null) || ("".equals(newValue) && oldValue == null)) {
            return false;
        }

        return true;
    }
    /**
     * 复制源对象中的字段值到目标对象中，如果字段名匹配的话。
     *
     * @param targetObject   目标对象，字段值将被复制到这个对象中。
     * @param sourceObject   源对象，字段值将从这个对象中获取。
     * @param fieldName      字段名称，用于匹配字段。
     * @param <T>            对象的类型。
     * @return               更新后的目标对象。
     */
    public static <T> T copyFieldValue(T targetObject, T sourceObject, String fieldName) {
        Field[] fields = sourceObject.getClass().getDeclaredFields();

        for (Field field : fields) {
            // 设置字段可访问
            field.setAccessible(true);

            // 检查字段名是否匹配
            if (field.getName().equals(fieldName)) {
                try {
                    // 从源对象获取字段值
                    Object value = field.get(sourceObject);

                    // 将值设置到目标对象中
                    field.set(targetObject, value);

                    System.out.println("字段名称: " + fieldName + ", 值: " + value);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        return targetObject;
    }
}
